-
Notifications
You must be signed in to change notification settings - Fork 219
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
热执行代码 #303
Conversation
WalkthroughThe recent updates to the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ProjectDynamicProxy
participant VSCSharpProcessor
participant VSCSharpFolder
User->>ProjectDynamicProxy: Run()
ProjectDynamicProxy->>VSCSharpProcessor: BuildProject()
VSCSharpProcessor->>VSCSharpFolder: Retrieve project paths
VSCSharpFolder-->>VSCSharpProcessor: Return paths
VSCSharpProcessor-->>ProjectDynamicProxy: Build complete
ProjectDynamicProxy->>VSCSharpProcessor: Run()
VSCSharpProcessor-->>ProjectDynamicProxy: Execution complete
ProjectDynamicProxy-->>User: Run complete
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
@coderabbitai review |
Actions performedReview triggered.
|
未检测到合适的 ISSUE 推荐给您。感谢您的反馈!
|
UT Test - Ubuntu1 tests 1 ✅ 0s ⏱️ Results for commit b680ec5. |
UT Test - Windows1 tests 1 ✅ 0s ⏱️ Results for commit b680ec5. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Outside diff range and nitpick comments (5)
src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/ProjectDynamicProxy.cs (5)
254-257
: Add documentation forBuildWithRelease
.Consider adding XML documentation comments to explain the purpose and effect of setting
IsRelease
to true.
259-261
: Add documentation forBuildWithDebug
.Consider adding XML documentation comments to explain the purpose and effect of setting
IsRelease
to false.
323-326
: Add documentation forAppendArgs(string arg)
.Consider adding XML documentation comments to explain the purpose and usage of this method.
327-330
: Add documentation forAppendArgs(params string[] args)
.Consider adding XML documentation comments to explain the purpose and usage of this method.
331-333
: Add documentation forClearArgs
.Consider adding XML documentation comments to explain the purpose and usage of this method.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (5)
- src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/Natasha.CSharp.Extension.HotExecutor.csproj (1 hunks)
- src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/ProjectDynamicProxy.cs (1 hunks)
- src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/VSCSharpFolder.cs (1 hunks)
- src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/VSCSharpProcessor.cs (1 hunks)
- src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/VSCSharpProjectFileWatcher.cs (1 hunks)
Additional comments not posted (5)
src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/Natasha.CSharp.Extension.HotExecutor.csproj (2)
3-15
: The updates to thePropertyGroup
are well-defined and align with the project's requirements for .NET Standard 2.0, including package metadata and compiler settings.
17-19
: The project reference toNatasha.CSharp.Compiler
is correctly specified.src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/VSCSharpFolder.cs (1)
16-30
: The static constructor correctly initializes all necessary paths using the current execution context, which is crucial for the dynamic nature of the project.src/Natasha.CSharp/Extension/Natasha.CSharp.Extension.HotExecutor/VSCSharpProcessor.cs (2)
14-28
: The constructor ofVSCSharpProcessor
is well-structured, initializing necessary fields and setting up the process start information correctly.
30-35
: TheResetOutputInfo
method dynamically configures the output paths and build arguments, which is essential for flexible project builds.
public static void Run(string proxyMethodName = "ProxyMain", string? argumentsMethodName= "ProxyMainArguments") | ||
{ | ||
_proxyMethodName = proxyMethodName; | ||
_argumentsMethodName = argumentsMethodName; | ||
var srcCodeFiles = Directory.GetFiles(VSCSharpFolder.MainCsprojPath, "*.cs", SearchOption.AllDirectories); | ||
|
||
foreach (var file in srcCodeFiles) | ||
{ | ||
if (CheckFileAvailiable(file)) | ||
{ | ||
var content = ReadFile(file); | ||
//Console.WriteLine(file); | ||
//Console.WriteLine(content); | ||
//Console.WriteLine("------------"); | ||
var tree = NatashaCSharpSyntax.ParseTree(content, null); | ||
var mainMethod = tree.GetRoot().DescendantNodes() | ||
.OfType<MethodDeclarationSyntax>() | ||
.FirstOrDefault(m => m.Identifier.Text == "Main"); | ||
|
||
if (mainMethod != null) | ||
{ | ||
ClassDeclarationSyntax? parentClass = mainMethod.Parent as ClassDeclarationSyntax ?? throw new Exception("获取 Main 方法类名出现错误!"); | ||
_className = parentClass.Identifier.Text; | ||
|
||
var proxyMethod = tree.GetRoot().DescendantNodes() | ||
.OfType<MethodDeclarationSyntax>() | ||
.FirstOrDefault(m => m.Identifier.Text == proxyMethodName); | ||
|
||
if (proxyMethod == null) | ||
{ | ||
throw new Exception($"{_className} 中未找到 {proxyMethodName} 代理方法!"); | ||
} | ||
_mainFile = file; | ||
} | ||
_fileCache[file] = tree; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor Run
method to improve readability and error handling.
The Run
method is quite lengthy and does multiple things. Consider breaking it down into smaller, more focused methods. Additionally, improve error handling by not just throwing exceptions but also logging them or handling them gracefully.
- throw new Exception($"{_className} 中未找到 {proxyMethodName} 代理方法!");
+ LogError($"Method {proxyMethodName} not found in class {_className}."); // Assuming LogError is a method that logs errors.
Committable suggestion was skipped due to low confidence.
private static string ReadFile(string file) | ||
{ | ||
FileStream stream; | ||
do | ||
{ | ||
try | ||
{ | ||
stream = new(file, FileMode.Open, FileAccess.Read, FileShare.Read); | ||
StringBuilder stringBuilder = new(); | ||
byte[] buffer = new byte[1024]; | ||
int bytesRead; | ||
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) | ||
{ | ||
stringBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); | ||
} | ||
stream.Dispose(); | ||
return stringBuilder.ToString(); | ||
} | ||
catch (Exception) | ||
{ | ||
#if DEBUG | ||
Console.WriteLine("命中文件锁!"); | ||
#endif | ||
|
||
Thread.Sleep(200); | ||
} | ||
|
||
|
||
} while (true); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor ReadFile
to handle exceptions more gracefully.
The current implementation of ReadFile
uses a loop that can potentially become an infinite loop if the file remains locked. Consider adding a maximum retry count and more detailed error logging.
- while (true)
+ for (int retry = 0; retry < 10; retry++)
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private static string ReadFile(string file) | |
{ | |
FileStream stream; | |
do | |
{ | |
try | |
{ | |
stream = new(file, FileMode.Open, FileAccess.Read, FileShare.Read); | |
StringBuilder stringBuilder = new(); | |
byte[] buffer = new byte[1024]; | |
int bytesRead; | |
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) | |
{ | |
stringBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); | |
} | |
stream.Dispose(); | |
return stringBuilder.ToString(); | |
} | |
catch (Exception) | |
{ | |
#if DEBUG | |
Console.WriteLine("命中文件锁!"); | |
#endif | |
Thread.Sleep(200); | |
} | |
} while (true); | |
private static string ReadFile(string file) | |
{ | |
FileStream stream; | |
for (int retry = 0; retry < 10; retry++) | |
{ | |
try | |
{ | |
stream = new(file, FileMode.Open, FileAccess.Read, FileShare.Read); | |
StringBuilder stringBuilder = new(); | |
byte[] buffer = new byte[1024]; | |
int bytesRead; | |
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) | |
{ | |
stringBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); | |
} | |
stream.Dispose(); | |
return stringBuilder.ToString(); | |
} | |
catch (Exception) | |
{ | |
#if DEBUG | |
Console.WriteLine("命中文件锁!"); | |
#endif | |
Thread.Sleep(200); | |
} | |
} | |
throw new IOException("Failed to read file after multiple attempts."); |
private static Task HotExecute() | ||
{ | ||
try | ||
{ | ||
_builderCache.WithRandomAssenblyName(); | ||
_builderCache.SyntaxTrees.Clear(); | ||
_builderCache.SyntaxTrees.AddRange(_fileCache.Values); | ||
if (IsRelease) | ||
{ | ||
_builderCache.WithReleaseCompile(); | ||
} | ||
else | ||
{ | ||
_builderCache.WithDebugCompile(); | ||
} | ||
var assembly = _builderCache.GetAssembly(); | ||
var types = assembly.GetTypes(); | ||
var typeInfo = assembly.GetTypeFromShortName(_className!); | ||
var methods = typeInfo.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | ||
if (methods.Any(item => item.Name == _argumentsMethodName)) | ||
{ | ||
_args.Clear(); | ||
var argumentMethodInfo = methods.First(item => item.Name == _argumentsMethodName); | ||
argumentMethodInfo.Invoke(null, []); | ||
} | ||
|
||
var proxyMethodInfo = typeInfo.GetMethod(_proxyMethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | ||
object? instance = null; | ||
if (!proxyMethodInfo.IsStatic) | ||
{ | ||
instance = Activator.CreateInstance(typeInfo); | ||
} | ||
if (proxyMethodInfo.GetParameters().Length == 1) | ||
{ | ||
proxyMethodInfo.Invoke(instance, [_args.ToArray()]); | ||
} | ||
else | ||
{ | ||
proxyMethodInfo.Invoke(instance, []); | ||
} | ||
|
||
} | ||
catch (Exception ex) | ||
{ | ||
Console.WriteLine(ex.Message); | ||
} | ||
return Task.CompletedTask; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure proper exception handling in HotExecute
.
The method HotExecute
catches a general exception and only logs the message. This might not be sufficient for debugging. Consider logging the full stack trace or handling specific exceptions differently.
- Console.WriteLine(ex.Message);
+ Console.WriteLine($"Error during hot execution: {ex}");
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private static Task HotExecute() | |
{ | |
try | |
{ | |
_builderCache.WithRandomAssenblyName(); | |
_builderCache.SyntaxTrees.Clear(); | |
_builderCache.SyntaxTrees.AddRange(_fileCache.Values); | |
if (IsRelease) | |
{ | |
_builderCache.WithReleaseCompile(); | |
} | |
else | |
{ | |
_builderCache.WithDebugCompile(); | |
} | |
var assembly = _builderCache.GetAssembly(); | |
var types = assembly.GetTypes(); | |
var typeInfo = assembly.GetTypeFromShortName(_className!); | |
var methods = typeInfo.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | |
if (methods.Any(item => item.Name == _argumentsMethodName)) | |
{ | |
_args.Clear(); | |
var argumentMethodInfo = methods.First(item => item.Name == _argumentsMethodName); | |
argumentMethodInfo.Invoke(null, []); | |
} | |
var proxyMethodInfo = typeInfo.GetMethod(_proxyMethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | |
object? instance = null; | |
if (!proxyMethodInfo.IsStatic) | |
{ | |
instance = Activator.CreateInstance(typeInfo); | |
} | |
if (proxyMethodInfo.GetParameters().Length == 1) | |
{ | |
proxyMethodInfo.Invoke(instance, [_args.ToArray()]); | |
} | |
else | |
{ | |
proxyMethodInfo.Invoke(instance, []); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine(ex.Message); | |
} | |
return Task.CompletedTask; | |
} | |
private static Task HotExecute() | |
{ | |
try | |
{ | |
_builderCache.WithRandomAssenblyName(); | |
_builderCache.SyntaxTrees.Clear(); | |
_builderCache.SyntaxTrees.AddRange(_fileCache.Values); | |
if (IsRelease) | |
{ | |
_builderCache.WithReleaseCompile(); | |
} | |
else | |
{ | |
_builderCache.WithDebugCompile(); | |
} | |
var assembly = _builderCache.GetAssembly(); | |
var types = assembly.GetTypes(); | |
var typeInfo = assembly.GetTypeFromShortName(_className!); | |
var methods = typeInfo.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | |
if (methods.Any(item => item.Name == _argumentsMethodName)) | |
{ | |
_args.Clear(); | |
var argumentMethodInfo = methods.First(item => item.Name == _argumentsMethodName); | |
argumentMethodInfo.Invoke(null, []); | |
} | |
var proxyMethodInfo = typeInfo.GetMethod(_proxyMethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); | |
object? instance = null; | |
if (!proxyMethodInfo.IsStatic) | |
{ | |
instance = Activator.CreateInstance(typeInfo); | |
} | |
if (proxyMethodInfo.GetParameters().Length == 1) | |
{ | |
proxyMethodInfo.Invoke(instance, [_args.ToArray()]); | |
} | |
else | |
{ | |
proxyMethodInfo.Invoke(instance, []); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine($"Error during hot execution: {ex}"); | |
} | |
return Task.CompletedTask; | |
} |
static string FindFileDirectory(DirectoryInfo csprojDirectory, string suffix) | ||
{ | ||
DirectoryInfo directory = new(csprojDirectory.FullName); | ||
while (directory != directory.Root) | ||
{ | ||
if (directory.GetFiles(suffix).Length > 0) | ||
{ | ||
return directory.FullName; | ||
} | ||
directory = directory.Parent; | ||
} | ||
throw new Exception("没有找到 sln 根文件!"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FindFileDirectory
method is robust in finding directories based on file suffixes. Consider enhancing the exception message to include the missing suffix for better debugging.
- throw new Exception("没有找到 sln 根文件!");
+ throw new Exception($"没有找到包含后缀 {suffix} 的根文件!");
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
static string FindFileDirectory(DirectoryInfo csprojDirectory, string suffix) | |
{ | |
DirectoryInfo directory = new(csprojDirectory.FullName); | |
while (directory != directory.Root) | |
{ | |
if (directory.GetFiles(suffix).Length > 0) | |
{ | |
return directory.FullName; | |
} | |
directory = directory.Parent; | |
} | |
throw new Exception("没有找到 sln 根文件!"); | |
} | |
static string FindFileDirectory(DirectoryInfo csprojDirectory, string suffix) | |
{ | |
DirectoryInfo directory = new(csprojDirectory.FullName); | |
while (directory != directory.Root) | |
{ | |
if (directory.GetFiles(suffix).Length > 0) | |
{ | |
return directory.FullName; | |
} | |
directory = directory.Parent; | |
} | |
throw new Exception($"没有找到包含后缀 {suffix} 的根文件!"); | |
} |
public ValueTask<bool> BuildProject() | ||
{ | ||
try | ||
{ | ||
ResetOutputInfo(); | ||
Encoding utf8 = Encoding.UTF8; | ||
using var process = new Process(); | ||
process.StartInfo = _builder; | ||
process.EnableRaisingEvents = true; | ||
process.OutputDataReceived += (sender, e) => | ||
{ | ||
if (!string.IsNullOrEmpty(e.Data)) | ||
{ | ||
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | ||
} | ||
}; | ||
process.ErrorDataReceived += (sender, e) => | ||
{ | ||
if (!string.IsNullOrEmpty(e.Data)) | ||
{ | ||
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | ||
} | ||
}; | ||
|
||
process.Start(); | ||
process.BeginOutputReadLine(); | ||
process.BeginErrorReadLine(); | ||
#if DEBUG | ||
Console.WriteLine($"正在执行 {_builder.FileName} {_builder.Arguments}"); | ||
#endif | ||
process.WaitForExit(); | ||
return new ValueTask<bool>(process.ExitCode == 0); | ||
} | ||
catch (Exception ex) | ||
{ | ||
#if DEBUG | ||
Console.WriteLine($"执行出错 {ex.Message}"); | ||
#endif | ||
} | ||
return new ValueTask<bool>(false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The BuildProject
method is robust, handling process execution and output effectively. Consider enhancing error handling by logging more specific error details or categorizing errors.
- Console.WriteLine($"执行出错 {ex.Message}");
+ Console.WriteLine($"执行出错: {ex.GetType().Name} - {ex.Message}");
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public ValueTask<bool> BuildProject() | |
{ | |
try | |
{ | |
ResetOutputInfo(); | |
Encoding utf8 = Encoding.UTF8; | |
using var process = new Process(); | |
process.StartInfo = _builder; | |
process.EnableRaisingEvents = true; | |
process.OutputDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | |
} | |
}; | |
process.ErrorDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | |
} | |
}; | |
process.Start(); | |
process.BeginOutputReadLine(); | |
process.BeginErrorReadLine(); | |
#if DEBUG | |
Console.WriteLine($"正在执行 {_builder.FileName} {_builder.Arguments}"); | |
#endif | |
process.WaitForExit(); | |
return new ValueTask<bool>(process.ExitCode == 0); | |
} | |
catch (Exception ex) | |
{ | |
#if DEBUG | |
Console.WriteLine($"执行出错 {ex.Message}"); | |
#endif | |
} | |
return new ValueTask<bool>(false); | |
} | |
public ValueTask<bool> BuildProject() | |
{ | |
try | |
{ | |
ResetOutputInfo(); | |
Encoding utf8 = Encoding.UTF8; | |
using var process = new Process(); | |
process.StartInfo = _builder; | |
process.EnableRaisingEvents = true; | |
process.OutputDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | |
} | |
}; | |
process.ErrorDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
Console.WriteLine(utf8.GetString(utf8.GetBytes(e.Data))); | |
} | |
}; | |
process.Start(); | |
process.BeginOutputReadLine(); | |
process.BeginErrorReadLine(); | |
#if DEBUG | |
Console.WriteLine($"正在执行 {_builder.FileName} {_builder.Arguments}"); | |
#endif | |
process.WaitForExit(); | |
return new ValueTask<bool>(process.ExitCode == 0); | |
} | |
catch (Exception ex) | |
{ | |
#if DEBUG | |
Console.WriteLine($"执行出错: {ex.GetType().Name} - {ex.Message}"); | |
#endif | |
} | |
return new ValueTask<bool>(false); | |
} |
public ValueTask<bool> Run() | ||
{ | ||
try | ||
{ | ||
if (_process != null) | ||
{ | ||
_process.Kill(); | ||
_process.Dispose(); | ||
|
||
} | ||
|
||
_process = new Process() | ||
{ | ||
StartInfo = new() | ||
{ | ||
FileName = VSCSharpFolder.ExecuteName, | ||
WorkingDirectory = _outpuNewAppFolder, | ||
CreateNoWindow = true, | ||
UseShellExecute = true, | ||
} | ||
}; | ||
#if DEBUG | ||
Console.WriteLine($"执行: {Path.Combine(_outpuNewAppFolder, VSCSharpFolder.ExecuteName)}"); | ||
#endif | ||
_process.Start(); | ||
// 等待一小段时间,让进程有机会启动 | ||
System.Threading.Thread.Sleep(1000); | ||
Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(_outputNewExeFile)); | ||
if (processes.Length > 0) | ||
{ | ||
return new ValueTask<bool>(true); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
Console.WriteLine("Error starting the process: " + e.Message); | ||
} | ||
return new ValueTask<bool>(false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Run
method effectively manages the execution of processes. Consider improving the process management logic to handle multiple instances more gracefully.
- if (processes.Length > 0)
+ if (processes.Any(p => p.HasExited == false))
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public ValueTask<bool> Run() | |
{ | |
try | |
{ | |
if (_process != null) | |
{ | |
_process.Kill(); | |
_process.Dispose(); | |
} | |
_process = new Process() | |
{ | |
StartInfo = new() | |
{ | |
FileName = VSCSharpFolder.ExecuteName, | |
WorkingDirectory = _outpuNewAppFolder, | |
CreateNoWindow = true, | |
UseShellExecute = true, | |
} | |
}; | |
#if DEBUG | |
Console.WriteLine($"执行: {Path.Combine(_outpuNewAppFolder, VSCSharpFolder.ExecuteName)}"); | |
#endif | |
_process.Start(); | |
// 等待一小段时间,让进程有机会启动 | |
System.Threading.Thread.Sleep(1000); | |
Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(_outputNewExeFile)); | |
if (processes.Length > 0) | |
{ | |
return new ValueTask<bool>(true); | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine("Error starting the process: " + e.Message); | |
} | |
return new ValueTask<bool>(false); | |
} | |
public ValueTask<bool> Run() | |
{ | |
try | |
{ | |
if (_process != null) | |
{ | |
_process.Kill(); | |
_process.Dispose(); | |
} | |
_process = new Process() | |
{ | |
StartInfo = new() | |
{ | |
FileName = VSCSharpFolder.ExecuteName, | |
WorkingDirectory = _outpuNewAppFolder, | |
CreateNoWindow = true, | |
UseShellExecute = true, | |
} | |
}; | |
#if DEBUG | |
Console.WriteLine($"执行: {Path.Combine(_outpuNewAppFolder, VSCSharpFolder.ExecuteName)}"); | |
#endif | |
_process.Start(); | |
// 等待一小段时间,让进程有机会启动 | |
System.Threading.Thread.Sleep(1000); | |
Process[] processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(_outputNewExeFile)); | |
if (processes.Any(p => p.HasExited == false)) | |
{ | |
return new ValueTask<bool>(true); | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine("Error starting the process: " + e.Message); | |
} | |
return new ValueTask<bool>(false); | |
} |
_notify = notifyAction; | ||
if (!watcherCache.ContainsKey(csprojPath)) | ||
{ | ||
if (File.Exists(csprojPath)) | ||
{ | ||
(FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher) watcher = new(); | ||
if (!watcherCache.TryAdd(csprojPath, watcher)) | ||
{ | ||
return; | ||
} | ||
|
||
#if DEBUG | ||
Console.WriteLine($"部署对 {csprojPath} 的监控. "); | ||
#endif | ||
var folder = Path.GetDirectoryName(csprojPath); | ||
var fileName = Path.GetFileName(csprojPath); | ||
var binFolder = Path.Combine(folder, "bin"); | ||
var objFolder = Path.Combine(folder, "obj"); | ||
FileSystemEventHandler handler = (sender, e) => { | ||
if (!e.FullPath.StartsWith(binFolder) && !e.FullPath.StartsWith(objFolder)) | ||
{ | ||
_notify?.Invoke(); | ||
} | ||
}; | ||
RenamedEventHandler reNameHandler = (sender, e) => { | ||
if (!e.FullPath.StartsWith(binFolder) && !e.FullPath.StartsWith(objFolder)) | ||
{ | ||
_notify?.Invoke(); | ||
} | ||
}; | ||
|
||
var csfileWatcher = new FileSystemWatcher() | ||
{ | ||
Path = folder, | ||
Filter = "*.cs", | ||
EnableRaisingEvents = true, | ||
IncludeSubdirectories = true | ||
}; | ||
csfileWatcher.Changed += handler; | ||
csfileWatcher.Deleted += handler; | ||
csfileWatcher.Created += handler; | ||
csfileWatcher.Renamed += reNameHandler; | ||
|
||
var csprojWatcher = new FileSystemWatcher() | ||
{ | ||
Path = folder, | ||
Filter = fileName, | ||
EnableRaisingEvents = true, | ||
IncludeSubdirectories = false, | ||
}; | ||
|
||
csprojWatcher.Changed += handler; | ||
csprojWatcher.Renamed += reNameHandler; | ||
csprojWatcher.Deleted += (sender, e) => | ||
{ | ||
if (e.FullPath == csprojPath) | ||
{ | ||
csprojWatcher.Dispose(); | ||
csfileWatcher.Dispose(); | ||
watcherCache.TryRemove(csprojPath, out var _); | ||
} | ||
}; | ||
watcherCache[csprojPath] = (csprojWatcher, csfileWatcher); | ||
MonitorDependencyProject(folder, csprojPath, watcherCache, notifyAction); | ||
} | ||
else | ||
{ | ||
throw new Exception($"未找到 {csprojPath} 文件"); | ||
} | ||
} | ||
} | ||
|
||
private void MonitorDependencyProject(string foldPath, string csprojPath, ConcurrentDictionary<string, (FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher)> watcherList, Action? notifyAction) | ||
{ | ||
try | ||
{ | ||
XmlDocument doc = new(); | ||
doc.Load(csprojPath); | ||
|
||
XmlNodeList projectReferenceNodes = doc.SelectNodes("//ProjectReference"); | ||
|
||
if (projectReferenceNodes != null) | ||
{ | ||
foreach (XmlNode node in projectReferenceNodes) | ||
{ | ||
var dependencyFilePath = node.Attributes["Include"].Value; | ||
if (dependencyFilePath.EndsWith(".csproj")) | ||
{ | ||
var newCsprojFilePath = Path.GetFullPath(Path.Combine(foldPath, dependencyFilePath)); | ||
new VSCSharpProjectFileInternalWatcher(newCsprojFilePath, watcherList, notifyAction); | ||
} | ||
} | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
Console.WriteLine("错误: " + e.Message); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VSCSharpProjectFileInternalWatcher
class is well-implemented, setting up watchers for project dependencies effectively. Enhance error handling by providing more specific error messages.
- Console.WriteLine("错误: " + e.Message);
+ Console.WriteLine($"错误: {e.GetType().Name} - {e.Message}");
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
internal class VSCSharpProjectFileInternalWatcher | |
{ | |
private Action? _notify; | |
public VSCSharpProjectFileInternalWatcher(string csprojPath, ConcurrentDictionary<string, (FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher)> watcherCache, Action? notifyAction = null) | |
{ | |
_notify = notifyAction; | |
if (!watcherCache.ContainsKey(csprojPath)) | |
{ | |
if (File.Exists(csprojPath)) | |
{ | |
(FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher) watcher = new(); | |
if (!watcherCache.TryAdd(csprojPath, watcher)) | |
{ | |
return; | |
} | |
#if DEBUG | |
Console.WriteLine($"部署对 {csprojPath} 的监控. "); | |
#endif | |
var folder = Path.GetDirectoryName(csprojPath); | |
var fileName = Path.GetFileName(csprojPath); | |
var binFolder = Path.Combine(folder, "bin"); | |
var objFolder = Path.Combine(folder, "obj"); | |
FileSystemEventHandler handler = (sender, e) => { | |
if (!e.FullPath.StartsWith(binFolder) && !e.FullPath.StartsWith(objFolder)) | |
{ | |
_notify?.Invoke(); | |
} | |
}; | |
RenamedEventHandler reNameHandler = (sender, e) => { | |
if (!e.FullPath.StartsWith(binFolder) && !e.FullPath.StartsWith(objFolder)) | |
{ | |
_notify?.Invoke(); | |
} | |
}; | |
var csfileWatcher = new FileSystemWatcher() | |
{ | |
Path = folder, | |
Filter = "*.cs", | |
EnableRaisingEvents = true, | |
IncludeSubdirectories = true | |
}; | |
csfileWatcher.Changed += handler; | |
csfileWatcher.Deleted += handler; | |
csfileWatcher.Created += handler; | |
csfileWatcher.Renamed += reNameHandler; | |
var csprojWatcher = new FileSystemWatcher() | |
{ | |
Path = folder, | |
Filter = fileName, | |
EnableRaisingEvents = true, | |
IncludeSubdirectories = false, | |
}; | |
csprojWatcher.Changed += handler; | |
csprojWatcher.Renamed += reNameHandler; | |
csprojWatcher.Deleted += (sender, e) => | |
{ | |
if (e.FullPath == csprojPath) | |
{ | |
csprojWatcher.Dispose(); | |
csfileWatcher.Dispose(); | |
watcherCache.TryRemove(csprojPath, out var _); | |
} | |
}; | |
watcherCache[csprojPath] = (csprojWatcher, csfileWatcher); | |
MonitorDependencyProject(folder, csprojPath, watcherCache, notifyAction); | |
} | |
else | |
{ | |
throw new Exception($"未找到 {csprojPath} 文件"); | |
} | |
} | |
} | |
private void MonitorDependencyProject(string foldPath, string csprojPath, ConcurrentDictionary<string, (FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher)> watcherList, Action? notifyAction) | |
{ | |
try | |
{ | |
XmlDocument doc = new(); | |
doc.Load(csprojPath); | |
XmlNodeList projectReferenceNodes = doc.SelectNodes("//ProjectReference"); | |
if (projectReferenceNodes != null) | |
{ | |
foreach (XmlNode node in projectReferenceNodes) | |
{ | |
var dependencyFilePath = node.Attributes["Include"].Value; | |
if (dependencyFilePath.EndsWith(".csproj")) | |
{ | |
var newCsprojFilePath = Path.GetFullPath(Path.Combine(foldPath, dependencyFilePath)); | |
new VSCSharpProjectFileInternalWatcher(newCsprojFilePath, watcherList, notifyAction); | |
} | |
} | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine("错误: " + e.Message); | |
} | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine($"错误: {e.GetType().Name} - {e.Message}"); | |
} |
internal class VSCSharpProjectFileWatcher | ||
{ | ||
private Func<Task>? _execute; | ||
private readonly HashSet<string> _mainExecuteCache; | ||
private long _timeStamp; | ||
private bool _needExecuted; | ||
private readonly ConcurrentDictionary<string, (FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher)> _projctWatcherCache; | ||
public VSCSharpProjectFileWatcher(string csprojPath, Func<Task>? executeAction) | ||
{ | ||
_execute = executeAction; | ||
_projctWatcherCache = []; | ||
_mainExecuteCache = []; | ||
_mainExecuteCache.Add(VSCSharpFolder.DebugPath); | ||
_mainExecuteCache.Add(VSCSharpFolder.ReleasePath); | ||
_mainExecuteCache.Add(VSCSharpFolder.ExecutePath); | ||
_ = new VSCSharpProjectFileInternalWatcher(csprojPath, _projctWatcherCache, Notify); | ||
_projctWatcherCache[csprojPath].csFileWatcher.Dispose(); | ||
} | ||
|
||
public void Notify() | ||
{ | ||
#if DEBUG | ||
Console.WriteLine($"接收重建通知!"); | ||
#endif | ||
_timeStamp = DateTime.UtcNow.Ticks / 10000; | ||
_needExecuted = true; | ||
} | ||
|
||
public void StartMonitor() | ||
{ | ||
|
||
Task.Run(async () => { | ||
|
||
while (true) | ||
{ | ||
if (_needExecuted == true) | ||
{ | ||
Clean(); | ||
#if DEBUG | ||
Console.WriteLine($"时间戳差值: {DateTime.UtcNow.Ticks / 10000 - _timeStamp}"); | ||
#endif | ||
if (DateTime.UtcNow.Ticks / 10000 - _timeStamp > 2000) | ||
{ | ||
_needExecuted = false; | ||
if (_execute!=null) | ||
{ | ||
await _execute(); | ||
} | ||
|
||
} | ||
await Task.Delay(800); | ||
} | ||
else | ||
{ | ||
await Task.Delay(800); | ||
} | ||
} | ||
}); | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VSCSharpProjectFileWatcher
class effectively initializes file watchers and handles notifications. Consider refining the delay logic to avoid hard-coded values.
- await Task.Delay(800);
+ await Task.Delay(TimeSpan.FromMilliseconds(800));
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
internal class VSCSharpProjectFileWatcher | |
{ | |
private Func<Task>? _execute; | |
private readonly HashSet<string> _mainExecuteCache; | |
private long _timeStamp; | |
private bool _needExecuted; | |
private readonly ConcurrentDictionary<string, (FileSystemWatcher csprojWatcher, FileSystemWatcher csFileWatcher)> _projctWatcherCache; | |
public VSCSharpProjectFileWatcher(string csprojPath, Func<Task>? executeAction) | |
{ | |
_execute = executeAction; | |
_projctWatcherCache = []; | |
_mainExecuteCache = []; | |
_mainExecuteCache.Add(VSCSharpFolder.DebugPath); | |
_mainExecuteCache.Add(VSCSharpFolder.ReleasePath); | |
_mainExecuteCache.Add(VSCSharpFolder.ExecutePath); | |
_ = new VSCSharpProjectFileInternalWatcher(csprojPath, _projctWatcherCache, Notify); | |
_projctWatcherCache[csprojPath].csFileWatcher.Dispose(); | |
} | |
public void Notify() | |
{ | |
#if DEBUG | |
Console.WriteLine($"接收重建通知!"); | |
#endif | |
_timeStamp = DateTime.UtcNow.Ticks / 10000; | |
_needExecuted = true; | |
} | |
public void StartMonitor() | |
{ | |
Task.Run(async () => { | |
while (true) | |
{ | |
if (_needExecuted == true) | |
{ | |
Clean(); | |
#if DEBUG | |
Console.WriteLine($"时间戳差值: {DateTime.UtcNow.Ticks / 10000 - _timeStamp}"); | |
#endif | |
if (DateTime.UtcNow.Ticks / 10000 - _timeStamp > 2000) | |
{ | |
_needExecuted = false; | |
if (_execute!=null) | |
{ | |
await _execute(); | |
} | |
} | |
await Task.Delay(800); | |
} | |
else | |
{ | |
await Task.Delay(800); | |
} | |
} | |
}); | |
} | |
public void StartMonitor() | |
{ | |
Task.Run(async () => { | |
while (true) | |
{ | |
if (_needExecuted == true) | |
{ | |
Clean(); | |
#if DEBUG | |
Console.WriteLine($"时间戳差值: {DateTime.UtcNow.Ticks / 10000 - _timeStamp}"); | |
#endif | |
if (DateTime.UtcNow.Ticks / 10000 - _timeStamp > 2000) | |
{ | |
_needExecuted = false; | |
if (_execute!=null) | |
{ | |
await _execute(); | |
} | |
} | |
await Task.Delay(TimeSpan.FromMilliseconds(800)); | |
} | |
else | |
{ | |
await Task.Delay(TimeSpan.FromMilliseconds(800)); | |
} | |
} | |
}); | |
} |
Summary by CodeRabbit
New Features
Enhancements